using System;

namespace IQIGame.Onigao.Game
{
    public struct Vector3Logic
    {
        public float x;
        public float y;
        public float z;

        private static readonly Vector3Logic s_Zero = new Vector3Logic(0f, 0f, 0f);
        private static readonly Vector3Logic s_One = new Vector3Logic(1f, 1f, 1f);
        private static readonly Vector3Logic s_Up = new Vector3Logic(0f, 1f, 0f);
        private static readonly Vector3Logic s_Down = new Vector3Logic(0f, -1f, 0f);
        private static readonly Vector3Logic s_Left = new Vector3Logic(-1f, 0f, 0f);
        private static readonly Vector3Logic s_Right = new Vector3Logic(1f, 0f, 0f);
        private static readonly Vector3Logic s_Forward = new Vector3Logic(0f, 0f, 1f);
        private static readonly Vector3Logic s_Back = new Vector3Logic(0f, 0f, -1f);

        public static Vector3Logic zero => s_Zero;
        public static Vector3Logic one => s_One;
        public static Vector3Logic up => s_Up;
        public static Vector3Logic down => s_Down;
        public static Vector3Logic left => s_Left;
        public static Vector3Logic right => s_Right;
        public static Vector3Logic forward => s_Forward;
        public static Vector3Logic back => s_Back;

        public Vector3Logic(float x, float y)
        {
            this.x = x;
            this.y = y;
            this.z = 0;
        }

        public Vector3Logic(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public void Set(float x, float y, float z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }

        public float sqrMagnitude
        {
            get
            {
                return x * x + y * y + z * z;
            }
        }

        public float magnitude
        {
            get
            {
                return (float)Math.Sqrt(sqrMagnitude);
            }
        }

        public Vector3Logic normalized
        {
            get
            {
                return Normalized(this);
            }
        }

        private static Vector3Logic Normalized(Vector3Logic v)
        {
            float magnitude = v.magnitude;
            if (magnitude > float.Epsilon)
            {
                return v / magnitude;
            }
            return zero;
        }

        public static Vector3Logic Min(Vector3Logic lhs, Vector3Logic rhs)
        {
            return new Vector3Logic(Math.Min(lhs.x, rhs.x), Math.Min(lhs.y, rhs.y), Math.Min(lhs.z, rhs.z));
        }

        /// <summary>
        ///   <para>Returns a vector that is made from the largest components of two vectors.</para>
        /// </summary>
        /// <param name="lhs"></param>
        /// <param name="rhs"></param>
        public static Vector3Logic Max(Vector3Logic lhs, Vector3Logic rhs)
        {
            return new Vector3Logic(Math.Max(lhs.x, rhs.x), Math.Max(lhs.y, rhs.y), Math.Max(lhs.z, rhs.z));
        }

        /// <summary>
        ///   <para>Multiplies two vectors component-wise.</para>
        /// </summary>
        /// <param name="a"></param>
        /// <param name="b"></param>
        public static Vector3Logic Scale(Vector3Logic a, Vector3Logic b)
        {
            return new Vector3Logic(a.x * b.x, a.y * b.y, a.z * b.z);
        }

        /// <summary>
        ///   <para>Multiplies every component of this vector by the same component of scale.</para>
        /// </summary>
        /// <param name="scale"></param>
        public void Scale(Vector3Logic scale)
        {
            x *= scale.x;
            y *= scale.y;
            z *= scale.z;
        }

        /// <summary>
        ///   <para>Clamps the Vector3Int to the bounds given by min and max.</para>
        /// </summary>
        /// <param name="min"></param>
        /// <param name="max"></param>
        public void Clamp(Vector3Logic min, Vector3Logic max)
        {
            x = Math.Max(min.x, x);
            x = Math.Min(max.x, x);
            y = Math.Max(min.y, y);
            y = Math.Min(max.y, y);
            z = Math.Max(min.z, z);
            z = Math.Min(max.z, z);
        }

        /// <summary>
        /// 欧拉角转方向向量（xy）
        /// </summary>
        /// <param name="euler">欧拉角，仅xy有效</param>
        /// <returns></returns>
        public static Vector3Logic EulerXyToDirection(Vector3Logic euler, bool requireNormalized = true)
        {
            float pitch = euler.x * MathLogic.Deg2Rad;
            float yaw = euler.y * MathLogic.Deg2Rad;

            double z = Math.Cos(pitch);
            double x = z * Math.Sin(yaw);
            double y = -Math.Sin(pitch);
            z = z * Math.Cos(yaw);

            Vector3Logic dir = new Vector3Logic((float)x, (float)y, (float)z);
            if (requireNormalized)
                return dir.normalized;
            return dir;
        }

        /// <summary>
        /// 单位方向矢量换算为欧拉角
        /// 注意这里默认z轴不会旋转，如果z轴会旋转，即实体会出现朝身体两侧旋转的情况，则此方法无效
        /// </summary>
        /// <param name="direction"></param>
        /// <returns></returns>
        public static Vector3Logic DirectionToEulerXy(Vector3Logic direction)
        {
            // Yaw
            double yaw = Math.Atan2(direction.x, direction.z) * MathLogic.Rad2Deg;
            double tempX = Math.Sqrt(1 - direction.y * direction.y);
            // Pitch
            double pitch = -Math.Acos(tempX) * MathLogic.Rad2Deg;

            if (direction.y < 0)
                pitch = -pitch;

            pitch = (pitch + 360) % 360;

            return new Vector3Logic((float)pitch, (float)yaw, 0);
        }

        public static Vector3Logic Lerp(Vector3Logic a, Vector3Logic b, float t)
        {
            t = MathLogic.Clamp01(t);
            return new Vector3Logic(a.x + (b.x - a.x) * t, a.y + (b.y - a.y) * t, a.z + (b.z - a.z) * t);
        }

        public static float Dot(Vector3Logic lhs, Vector3Logic rhs)
        {
            return lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z;
        }

        public static implicit operator Framework.Vector3Int(Vector3Logic v)
        {
            return new Framework.Vector3Int((int)v.x, (int)v.y, (int)v.z);
        }

        public static implicit operator Vector3Logic(Framework.Vector3Int v)
        {
            return new Vector3Logic(v.x, v.y, v.z);
        }

        public static Vector3Logic operator +(Vector3Logic a, Vector3Logic b)
        {
            return new Vector3Logic(a.x + b.x, a.y + b.y, a.z + b.z);
        }

        public static Vector3Logic operator -(Vector3Logic a, Vector3Logic b)
        {
            return new Vector3Logic(a.x - b.x, a.y - b.y, a.z - b.z);
        }

        public static Vector3Logic operator *(Vector3Logic a, Vector3Logic b)
        {
            return new Vector3Logic(a.x * b.x, a.y * b.y, a.z * b.z);
        }

        public static Vector3Logic operator -(Vector3Logic a)
        {
            return new Vector3Logic(-a.x, -a.y, -a.z);
        }

        public static Vector3Logic operator *(Vector3Logic a, float b)
        {
            return new Vector3Logic(a.x * b, a.y * b, a.z * b);
        }

        public static Vector3Logic operator *(float a, Vector3Logic b)
        {
            return new Vector3Logic(a * b.x, a * b.y, a * b.z);
        }

        public static Vector3Logic operator /(Vector3Logic a, float b)
        {
            return new Vector3Logic(a.x / b, a.y / b, a.z / b);
        }

        public static bool operator ==(Vector3Logic lhs, Vector3Logic rhs)
        {
            return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z;
        }

        public static bool operator !=(Vector3Logic lhs, Vector3Logic rhs)
        {
            return !(lhs == rhs);
        }

        /// <summary>
        ///   <para>Returns true if the objects are equal.</para>
        /// </summary>
        /// <param name="other"></param>
        public override bool Equals(object other)
        {
            if (!(other is Vector3Logic))
            {
                return false;
            }
            return Equals((Vector3Logic)other);
        }

        public bool Equals(Vector3Logic other)
        {
            return this == other;
        }

        /// <summary>
        ///   <para>Gets the hash code for the Vector3Int.</para>
        /// </summary>
        /// <returns>
        ///   <para>The hash code of the Vector3Int.</para>
        /// </returns>
        public override int GetHashCode()
        {
            int hashCode = y.GetHashCode();
            int hashCode2 = z.GetHashCode();
            return x.GetHashCode() ^ (hashCode << 4) ^ (hashCode >> 28) ^ (hashCode2 >> 4) ^ (hashCode2 << 28);
        }

        /// <summary>
        ///   <para>Returns a formatted string for this vector.</para>
        /// </summary>
        /// <param name="format">A numeric format string.</param>
        /// <param name="formatProvider">An object that specifies culture-specific formatting.</param>
        public override string ToString()
        {
            return $"[{x}, {y}, {z}]";
        }
    }
}
